using System;

namespace Org.BouncyCastle.Crypto.Digests
{
	/**
	* base implementation of MD4 family style digest as outlined in
	* "Handbook of Applied Cryptography", pages 344 - 347.
	*/
	public abstract class GeneralDigest
		: IDigest
	{
		private const int BYTE_LENGTH = 64;

		private byte[]  xBuf;
		private int     xBufOff;

		private long    byteCount;

		internal GeneralDigest()
		{
			xBuf = new byte[4];
		}

		internal GeneralDigest(GeneralDigest t)
		{
			xBuf = new byte[t.xBuf.Length];
			Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);

			xBufOff = t.xBufOff;
			byteCount = t.byteCount;
		}

		public void Update(byte input)
		{
			xBuf[xBufOff++] = input;

			if (xBufOff == xBuf.Length)
			{
				ProcessWord(xBuf, 0);
				xBufOff = 0;
			}

			byteCount++;
		}

		public void BlockUpdate(
			byte[]  input,
			int     inOff,
			int     length)
		{
			//
			// fill the current word
			//
			while ((xBufOff != 0) && (length > 0))
			{
				Update(input[inOff]);
				inOff++;
				length--;
			}

			//
			// process whole words.
			//
			while (length > xBuf.Length)
			{
				ProcessWord(input, inOff);

				inOff += xBuf.Length;
				length -= xBuf.Length;
				byteCount += xBuf.Length;
			}

			//
			// load in the remainder.
			//
			while (length > 0)
			{
				Update(input[inOff]);

				inOff++;
				length--;
			}
		}

		public void Finish()
		{
			long    bitLength = (byteCount << 3);

			//
			// add the pad bytes.
			//
			Update((byte)128);

			while (xBufOff != 0) Update((byte)0);
			ProcessLength(bitLength);
			ProcessBlock();
		}

		public virtual void Reset()
		{
			byteCount = 0;
			xBufOff = 0;
			Array.Clear(xBuf, 0, xBuf.Length);
		}

		public int GetByteLength()
		{
			return BYTE_LENGTH;
		}

		internal abstract void ProcessWord(byte[] input, int inOff);
		internal abstract void ProcessLength(long bitLength);
		internal abstract void ProcessBlock();
		public abstract string AlgorithmName { get; }
		public abstract int GetDigestSize();
		public abstract int DoFinal(byte[] output, int outOff);
	}
}
